package is.glif.fenius.translators.argia;

import is.glif.fenius.api.FActivationStatus;
import is.glif.fenius.api.FAvailReq;
import is.glif.fenius.api.FAvailResp;
import is.glif.fenius.api.FCancelReq;
import is.glif.fenius.api.FCancelResp;
import is.glif.fenius.api.FCreateReq;
import is.glif.fenius.api.FCreateResp;
import is.glif.fenius.api.FExternalFault;
import is.glif.fenius.api.FInternalFault;
import is.glif.fenius.api.FListReq;
import is.glif.fenius.api.FListResp;
import is.glif.fenius.api.FQueryReq;
import is.glif.fenius.api.FQueryResp;
import is.glif.fenius.api.FReservation;
import is.glif.fenius.api.FReservationReqParams;
import is.glif.fenius.api.FReservationStatus;
import is.glif.fenius.api.Translator;
import is.glif.fenius.translators.argia.beans.Reservation;
import is.glif.fenius.translators.argia.db.ReservationDAO;
import is.glif.fenius.translators.argia.utils.AdvReservationsWrapper;
import is.glif.fenius.translators.argia.utils.auth.ApplianceLogin;
import is.glif.fenius.translators.argia.utils.auth.ProxyUtil;
import is.glif.fenius.util.ConfigManager;

import java.lang.reflect.InvocationTargetException;
import java.net.Proxy;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.globus.axis.util.Util;

import ca.inocybe.argia.stubs.reservation.CancelReservation;
import ca.inocybe.argia.stubs.reservation.CancelReservationResponse;
import ca.inocybe.argia.stubs.reservation.CancelReservationType;
import ca.inocybe.argia.stubs.reservation.ConnectionConstraintType;
import ca.inocybe.argia.stubs.reservation.CreateReservation;
import ca.inocybe.argia.stubs.reservation.CreateReservationResponse;
import ca.inocybe.argia.stubs.reservation.CreateReservationType;
import ca.inocybe.argia.stubs.reservation.EndpointType;
import ca.inocybe.argia.stubs.reservation.FixedReservationConstraintType;
import ca.inocybe.argia.stubs.reservation.ReservationType;
import ca.inocybe.argia.stubs.reservation.ServiceConstraintType;

/**
 * Argia Translator
 * 
 * @author Ali LAHLOU (Synchromedia, ETS)
 * 
 */
public class ArgiaTranslator implements Translator {

	static {
		Util.registerTransport();
	}

	// Logger
	private Log logger = LogFactory.getLog(ArgiaTranslator.class);
	// Argia config
	private Map<String, Object> argiaConfig;
	private String host;
	private String username;
	private String password;
	private String organization;
	private int maxDelay;

	private Proxy proxy = null;

	@SuppressWarnings("unchecked")
	public ArgiaTranslator() {

		ConfigManager cm = ConfigManager.getInstance();
		Map<String, Object> config = cm.getConfiguration();
		argiaConfig = (Map<String, Object>) config.get("argia");
		host = (String) argiaConfig.get("host");
		username = (String) argiaConfig.get("username");
		password = (String) argiaConfig.get("password");
		organization = (String) argiaConfig.get("organization");
		maxDelay = (Integer) argiaConfig.get("maxDelay");

		AdvReservationsWrapper.setHost(host);
		AdvReservationsWrapper.setOrganization(organization);

		proxy = ProxyUtil.getDirectProxy();

		try {
			ApplianceLogin.login(host, username, password, proxy);
		} catch (InvocationTargetException e) {
			System.out.println("Error Logging into appliance");
			e.printStackTrace();
			System.exit(0);
		}

	}

	public FCreateResp create(FCreateReq resrvReq) throws FInternalFault,
			FExternalFault {

		System.out.println("***FW*** Create request received");
		/***** Fenius specific code ****/
		FReservationReqParams rReqParams = resrvReq.getResvParams();
		
		/***** END Fenius specific code ****/

		System.out.println("Attempting to create connection between "
				+ rReqParams.getSrcEndpoint() + " and "
				+ rReqParams.getDstEndpoint());

		long rid = 0;
		ServiceConstraintType[] services = new ServiceConstraintType[1];
		ServiceConstraintType service = new ServiceConstraintType();
		service.setServiceID(1);
		service.setTypeOfReservation(ReservationType.fromValue("fixed"));
		FixedReservationConstraintType fixedReservation = new FixedReservationConstraintType();
		GregorianCalendar calendar = new GregorianCalendar();
		calendar.setTime(new Date());
		fixedReservation.setStartTime(calendar);
		fixedReservation.setDuration(rReqParams.getDuration());
		fixedReservation.setTimeZone(calendar.getTimeZone().getDisplayName());
		logger.debug("setting start time to: " + calendar.getTime());
		service.setFixedReservationConstraints(fixedReservation);
		service.setAutomaticActivation(true);
		ConnectionConstraintType[] connections = new ConnectionConstraintType[1];
		ConnectionConstraintType connection = new ConnectionConstraintType();
		connection.setConnectionID(1);
		connection.setDirectionality(0);
		EndpointType source = new EndpointType();
		source.setEndpointId(rReqParams.getSrcEndpoint());
		connection.setSource(source);
		EndpointType target = new EndpointType();
		target.setEndpointId(rReqParams.getDstEndpoint());
		EndpointType[] targets = new EndpointType[1];
		targets[0] = target;
		connection.setTarget(targets);

		// put the vlan ID in the max Delay field so it can be passed to Argia
		connection.setMaxDelay(new Integer(rReqParams.getSrcVlanTag()));

		connection.setMaxBW(rReqParams.getBandwidth());
		connection.setMinBW(rReqParams.getBandwidth());
		connections[0] = connection;
		service.setConnections(connections);
		services[0] = service;

		CreateReservation crReq = new CreateReservation();
		CreateReservationType crReqT = new CreateReservationType();
		crReqT.setJobID(null);
		crReqT.setService(services);

		crReq.setCreateReservation(crReqT);

		/***** Fenius specific code ****/
		FCreateResp resp = new FCreateResp();
		FReservation resrv = new FReservation();

		resrv.setSrcEndpoint(rReqParams.getSrcEndpoint());
		resrv.setDstEndpoint(rReqParams.getDstEndpoint());
		resrv.setActivationTime(calendar.getTime());
		resrv.setDuration(rReqParams.getDuration());
		resrv.setBandwidth(rReqParams.getBandwidth());
//		Long now = System.currentTimeMillis() / 1000L;
//		String reservationId = "argia-resv-" + now.toString();
//		resrv.setReservationId(reservationId);
		resrv.setDstVlanTag(rReqParams.getDstVlanTag());
		resrv.setSrcVlanTag(rReqParams.getSrcVlanTag());
		/***** END Fenius specific code ****/

		CreateReservationResponse crResp = null;
		try {
			crResp = AdvReservationsWrapper.createReservation(crReq);

			rid = crResp.getCreateReservationResponse().getReservationID();
			System.out
					.println("     -> Reservation created successfully! Identifier: "
							+ rid);

			/***** Fenius specific code ****/
			resrv.setReservationStatus(FReservationStatus.RESERVED);
			resrv.setActivationStatus(FActivationStatus.ACTIVE);
			resrv.setReservationId(Long.toString(rid));
			resrv.setReservationId(new Long(rid).toString());
			resp.setResvParams(resrv);
			/***** END Fenius specific code ****/

			// persistence
			logger.debug("starting to save reservation");
			Reservation argiaRsv = new Reservation();
			argiaRsv.setHost(host);
			argiaRsv.setUsername(username);
			
			//TODO after Glif, remove the two Res IDs and use only the Argia ID.
			argiaRsv.setArgiaID(rid);
			argiaRsv.setFeniusID(new Long(rid).toString());
			argiaRsv.setSrcEndpoint(rReqParams.getSrcEndpoint());
			argiaRsv.setDestEndpoint(rReqParams.getDstEndpoint());
			argiaRsv.setStartTime(calendar.getTime());
			argiaRsv.setDuration(rReqParams.getDuration());
			argiaRsv.setMaxDelay(maxDelay);
			argiaRsv.setMaxBW(rReqParams.getBandwidth());
			argiaRsv.setMinBW(rReqParams.getBandwidth());
			argiaRsv.setActivationStatus(FActivationStatus.ACTIVE.toString());
			argiaRsv.setReservationStatus(FReservationStatus.RESERVED
					.toString());
			argiaRsv.setSrcVlanTag(rReqParams.getSrcVlanTag());
			argiaRsv.setDstVlanTag(rReqParams.getDstVlanTag());
			ReservationDAO dao = new ReservationDAO();
			dao.create(argiaRsv);
			logger.debug("done saving reservation");

		} catch (RemoteException e) {
			/***** Fenius specific code ****/
			resrv.setReservationStatus(FReservationStatus.FAILED);
			resrv.setActivationStatus(FActivationStatus.FAILED);
			resp.setResvParams(resrv);
			/***** END Fenius specific code ****/

			e.printStackTrace();
			System.out.println("     -> Reservation NOT possible.");
		}

		System.out.println("***FW*** Now returning from Create");

		return resp;
	}


	public FCancelResp cancel(FCancelReq r) throws FInternalFault,
			FExternalFault {

		ReservationDAO dao = new ReservationDAO();
		Reservation res = dao.find(r.getReservationId());

		if (res != null) {
			CancelReservation cancelresv = new CancelReservation();
			CancelReservationType crType = new CancelReservationType();
			crType.setReservationID(res.getArgiaID());
			cancelresv.setCancelReservation(crType);

			CancelReservationResponse crResp = new CancelReservationResponse();
			try {
				crResp = AdvReservationsWrapper.cancelReservation(cancelresv);
				if (crResp.getCancelReservationResponse().isSuccess()) {

					// Persistence Update
					res.setActivationStatus(FActivationStatus.INACTIVE
							.toString());
					dao.update(res);

					System.out
							.println("     -> Reservation canceled successfully.");

				} else {
					System.out.println("     -> Reservation NOT canceled.");
				}
			} catch (RemoteException e) {
				e.printStackTrace();
				System.out
						.println("     -> Reservation NOT canceled. Some error occurred");
			}
		}

		return null;
	}

	
	public FListResp list(FListReq r) throws FInternalFault, FExternalFault {

		ReservationDAO dao = new ReservationDAO();
		List<Reservation> list = dao.findAll();

		FListResp resp = new FListResp();

		List<FReservation> listResv = new ArrayList<FReservation>();

		for (Reservation re : list) {
			FReservation resrv = reservationToFReservation(re);

			Date end = new Date(
					(long) (resrv.getActivationTime().getTime() + resrv
							.getDuration()));

			Boolean isEnd = false;
			if (r.getStartingAfter() != null
					&& resrv.getActivationTime().before(r.getStartingAfter())) {
				isEnd = true;
			}
			if (r.getStartingBefore() != null
					&& resrv.getActivationTime().after(r.getStartingBefore())) {
				isEnd = true;
			}
			if (r.getEndingAfter() != null && end.before(r.getEndingAfter())) {
				isEnd = true;
			}
			if (r.getEndingBefore() != null && end.after(r.getEndingBefore())) {
				isEnd = true;
			}

			if (!isEnd) {

				if ((r.getReservationIds() == null || r.getReservationIds().size() == 0)
						&& (r.getStatuses() == null || r.getStatuses().size() == 0)) {
					listResv.add(resrv);
				} else if ((r.getReservationIds() == null || r.getReservationIds().size() == 0)
						&& r.getStatuses().contains(resrv.getReservationStatus().toString())) {
					listResv.add(resrv);

				} else if ((r.getStatuses() == null || r.getStatuses().size() == 0)
						&& r.getReservationIds().contains(resrv.getReservationId())) {
					listResv.add(resrv);

				} else if (r.getStatuses().contains(resrv.getReservationStatus().toString())
						&& r.getReservationIds().contains(resrv.getReservationId())) {
					listResv.add(resrv);
				}
			}
		}

		resp.setReservations(listResv);
		return resp;
	}

	/**
	 * Query for a specific reservation 
	 */
	public FQueryResp query(FQueryReq r) throws FInternalFault, FExternalFault {
		ReservationDAO dao = new ReservationDAO();
		
		String reservId = r.getReservationId();
		Reservation reservation = dao.find(reservId);
		FReservation fReserv = reservationToFReservation(reservation);
		
		FQueryResp resp = new FQueryResp();
		resp.setResvParams(fReserv);
		
		return resp;
	}
	

	public FAvailResp isAvailable(FAvailReq r) throws FInternalFault,
			FExternalFault {
		// TODO Auto-generated method stub
		return null;
	}
	
	/*
	 * Translate from a Reservation type, which is the Argia translator's representation of a reservation into
	 * an FReservation type.
	 */
	private FReservation reservationToFReservation(Reservation re) {
		FReservation resrv = new FReservation();
		resrv.setActivationTime(re.getStartTime());
		resrv.setDstEndpoint(re.getDestEndpoint());
		resrv.setDuration(re.getDuration());
		resrv.setReservationId(re.getFeniusID());
		resrv.setSrcEndpoint(re.getSrcEndpoint());
		resrv.setActivationStatus(FActivationStatus.fromValue(re
				.getActivationStatus()));
		resrv.setReservationStatus(FReservationStatus.fromValue(re
				.getReservationStatus()));
		resrv.setBandwidth(re.getMaxBW());
		resrv.setSrcVlanTag(re.getSrcVlanTag());
		resrv.setDstVlanTag(re.getDstVlanTag());
		
		return resrv;
	}

}
